package org.toves.checklua.ast;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;

import org.toves.checklua.scan.Token;


public class AstNode {	
	public static final AstNodeType BINOP = new AstNodeType("binop");
	public static final AstNodeType UNOP = new AstNodeType("unop");
	public static final AstNodeType NAME = new AstNodeType("name");
	public static final AstNodeType CALL = new AstNodeType("call");
	public static final AstNodeType INDEX = new AstNodeType("index");
	public static final AstNodeType FIELD = new AstNodeType("field");
	public static final AstNodeType METHOD = new AstNodeType("methodcall");
	public static final AstNodeType CONST = new AstNodeType("const");
	public static final AstNodeType DEF_FUNC_ANON = new AstNodeType("defanonfunc");
	public static final AstNodeType CONSTRUCTOR = new AstNodeType("constructor");

	public static final AstNodeType EMPTY = new AstNodeType("empty");
	public static final AstNodeType SEQ = new AstNodeType("seq");
	public static final AstNodeType ASSN = new AstNodeType("assn");
	public static final AstNodeType ASSN_LOCAL = new AstNodeType("assn_local");
	public static final AstNodeType DEF_FUNC = new AstNodeType("def_func");
	public static final AstNodeType DEF_FUNC_LOCAL = new AstNodeType("def_local_func");
	public static final AstNodeType CALL_AS_STMT = new AstNodeType("call_stmt");
	public static final AstNodeType DO = new AstNodeType("do");
	public static final AstNodeType IF = new AstNodeType("if");
	public static final AstNodeType WHILE = new AstNodeType("while");
	public static final AstNodeType REPEAT = new AstNodeType("repeat");
	public static final AstNodeType FOR_INTS = new AstNodeType("for_ints");
	public static final AstNodeType FOR_EACH = new AstNodeType("for_each");
	public static final AstNodeType RETURN = new AstNodeType("return");
	public static final AstNodeType BREAK = new AstNodeType("break");

	private static final Object[] EMPTY_PARMS = new Object[0];
	private static final int FLAG_DEFINE = 1;
	private static final int FLAG_DEFINE_LOCAL = 3;
	
	private static final HashSet<AstNodeType> STMT_TYPES = new HashSet<AstNodeType>();
	
	static {
		AstNodeType[] stmtTypes = { ASSN, ASSN_LOCAL, DEF_FUNC, DEF_FUNC_LOCAL,
				CALL_AS_STMT, DO, IF, WHILE, REPEAT, FOR_INTS, FOR_EACH,
				RETURN, BREAK };
		for (AstNodeType op : stmtTypes) {
			STMT_TYPES.add(op);
		}
	}
	
	private AstNodeType type;
	private int flags;
	private Token token;
	private Object[] parms;
	
	AstNode(AstNodeType type, Token token, Object[] parms) {
		this.type = type;
		this.flags = 0;
		this.token = token;
		this.parms = parms == null ? EMPTY_PARMS : parms;
	}
	
	public Iterable<AstNode> getChildren() {
		return new AstChildren(this);
	}
	
	public Object getNodeType() {
		return type;
	}
	
	public Token getRepresentativeToken() {
		return token;
	}
	
	public List<Object> getParameters() {
		return Collections.unmodifiableList(Arrays.asList(parms));
	}
	
	public int getParameterCount() {
		return parms.length;
	}
	
	public Object getParameter(int index) {
		Object[] ps = parms;
		int len = ps.length;
		if (index < 0 || index >= len) {
			if(len == 0) {
				throw new IndexOutOfBoundsException("no AST parameters exist");
			} else {
				throw new IndexOutOfBoundsException("AST parameter index must "
						+ "be at most " + (len - 1) + ", but given " + index);
			}
		}
		return ps[index];
	}
	
	void setDefineGlobal() {
		flags |= FLAG_DEFINE;
	}
	
	void setDefineLocal() {
		flags |= FLAG_DEFINE_LOCAL;
	}
	
	public boolean isStatement() {
		return STMT_TYPES.contains(type);
	}
	
	public boolean isDefineGlobal() {
		return (flags & FLAG_DEFINE_LOCAL) == FLAG_DEFINE;
	}
	
	public boolean isDefineLocal() {
		return (flags & FLAG_DEFINE_LOCAL) == FLAG_DEFINE_LOCAL;
	}
	
	public boolean isRead() {
		return (flags & FLAG_DEFINE) == 0;
	}
}
